package cn.microven.mindmap.components.part;

import cn.microven.mindmap.components.control.MapTreeItem;
import cn.microven.mindmap.components.control.NodeContextMenu;
import cn.microven.mindmap.components.line.MapLine;
import cn.microven.mindmap.components.node.AbstractMapNode;
import cn.microven.mindmap.oprations.AddLeafOperation;
import cn.microven.mindmap.oprations.RemoveLeafOperation;
import cn.microven.mindmap.struture.MindMap;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.binding.NumberBinding;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.util.Pair;

/**
 * MapPart 是用于组织节点的第一层容器，包含添加节点、删除节点，同时操作连线的功能
 * 在一个 MapPart 对象中只存在父子两层关系。
 * 包含直接连接的子节点和父节点，参考数据结构——树
 *
 * @author 吴博维
 * @version 1.0
 */
public class MapPart extends HBox implements Part {


    /**
     * 树状图
     */
    protected MapTreeItem treeItem;

    /**
     * 父节点
     */
    protected Part parentPart;

    /**
     * 根节点
     */
    protected AbstractMapNode root;

    /**
     * 子节点以及其连线
     * key为连线 value为子节点
     */
    protected SimpleListProperty<Pair<MapLine, Part>> leaves ;

    /**
     * AnchorPane
     * 连线区域
     */
    protected Pane linesBox;

    /**
     * VBox
     * 负责子节点的纵向布局
     */
    public VBox vBox;

    /**
     * 与根节点的连接点
     */
    protected NumberBinding leftIntersectionY;

    /**
     * 与叶子节点的连接点
     */
    protected DoubleBinding rightIntersectionY;

    /**
     * 右键菜单
     */
    protected NodeContextMenu context;

    protected int level = 1;

    /**
     * 构造方法
     * @param root 根节点
     */
    public MapPart(AbstractMapNode root) {

        //初始化组件
        vBox = new VBox();
        linesBox = new Pane();
        this.root = root;
        this.setPrefHeight(0);

        //初始化 leaves 列表
        ObservableList<Pair<MapLine, Part>> observableLeaf = FXCollections.observableArrayList();
        this.leaves = new SimpleListProperty<Pair<MapLine, Part>>(observableLeaf);

        //添加右键菜单
        context = new NodeContextMenu(this);
        //添加树状图
        treeItem = new MapTreeItem(this);

        initLayout();
        //布局调整
        vBox.setSpacing(10);


        //样式调整
        this.setFillHeight(false);
        linesBox.setPrefWidth(100);

        bindIntersection();

        //绑定vBox高度和linesBox高度
        vBox.heightProperty().addListener(((observableValue, number, t1) ->{
            linesBox.setPrefHeight((Double)t1);
        }));
//        linesBox.setOnMouseClicked((mouseEvent -> {
//            MindMap.getInstance().clearSelect();
//        }));

        //当根结点溢出时保持与根结点高度同步
        root.heightProperty().addListener(((observableValue, number, t1) -> {
            if (vBox.getHeight()<root.getHeight()) {
                vBox.setPrefHeight(root.getHeight());
            }
        }));

        //连线区域是否可见由vBox决定
        linesBox.visibleProperty().bind(vBox.visibleProperty());
        linesBox.managedProperty().bind(vBox.managedProperty());
        treeItem.expandedProperty().bindBidirectional(vBox.visibleProperty());


        leavesProperty().addListener((ListChangeListener<? super Pair<MapLine, Part>>) change -> {
            //自动调整连线区域的宽度
            if (leaves.getSize() > 5){
                double newWidth = 100 + 20*leaves.getSize()/5.0;
                linesBox.setPrefWidth(newWidth);
            }
            else {
                //最小宽度是100
                linesBox.setPrefWidth(100);
            }
        });
    }


    public int getLevel() {
        return this.level;
    }

    @Override
    public void setLevel(int level) {
        this.level = level;
    }


    public MapPart(AbstractMapNode node,Part parentPart) {

    }

    /**
     * 连接点绑定
     */
    protected void bindIntersection(){
        //连接点属性绑定
        rightIntersectionY = this.root.layoutYProperty().add(this.root.heightProperty().divide(2));
        leftIntersectionY =  this.layoutYProperty().add(this.root.layoutYProperty()).add(this.root.heightProperty().divide(2));
    }


    /**
     * 初始化布局
     */
    protected void initLayout(){
        this.getChildren().addAll(root,linesBox,vBox);
        this.setAlignment(Pos.CENTER_LEFT);
        vBox.setAlignment(Pos.CENTER_LEFT);
    }

    /**
     * 添加子节点
     * 子节点必须是已经初始化的 MapPart 类型
     *
     * @param nodePart 子节点
     */
    public void addLeaf(Part nodePart) {
        //添加节点
        MapPart node = (MapPart) nodePart;
        MapLine line = connectLine(node);

        AddLeafOperation alo = new AddLeafOperation(this, leavesProperty(), vBox,linesBox,new Pair<>(line, node));
        alo.start();
    }

    public void addLeaf() {
        MapPart node = new MapPart(MindMap.getInstance().getDefaultNode().clone());
        this.addLeaf(node);
    }

    public NumberBinding getRightIntersectionY() {
        return rightIntersectionY;
    }

    public DoubleBinding rightIntersectionYProperty() {
        return rightIntersectionY;
    }

    public MapLine connectLine(Part node) {
        //初始化连线连线
        MapLine line = new MapLine();
//        linesBox.getChildren().addAll(line);

        //设置控制点
        line.getQuad().controlXProperty().bind(line.startXProperty().add(line.endXProperty().divide(10)));

        //设置开始横坐标
        line.setStartX(2);
        line.endXProperty().bind(linesBox.widthProperty().subtract(2));

        //绑定连线的起点和终点，与节点的连接点相对应
        line.startYProperty().bind(this.rightIntersectionY);
        line.endYProperty().bind(node.getLeftIntersectionY());

        return line;
    }

    /**
     * 删除子节点
     * 该操作会同时删除子节点的后代节点·
     *
     * @param node 删除的节点
     */
    public void removeLeaf(Part node) {
        //leaves.removeIf(child -> child.getValue() == node);
        Pair<MapLine, Part> leaf = null;
        for(Pair<MapLine, Part> pair : leaves) {
            if (pair.getValue() == node) {
                leaf = pair;
            }
        }
        RemoveLeafOperation rlo = new RemoveLeafOperation(this, leavesProperty(), vBox,linesBox,leaf);
        rlo.start();
    }

    @Override
    public String getType() {
        return "RightMapPart";
    }


    /**
     * 设置子节点可见性
     * 该操作会影响所有后代节点
     *
     * @param visible 后代节点可见性
     */
    public void setChildrenVisible(boolean visible) {
        this.vBox.setVisible(visible);
        this.vBox.setManaged(visible);
        this.layout();
    }

    /**
     * 获取子节点可见性
     *
     * @return 子节点可见性
     */
    public boolean isChildrenVisible() {
        return this.vBox.isVisible();
    }

    /**
     * 切换子节点可见性
     */
    public void toggleChildrenVisible() {
        this.vBox.setVisible(!vBox.isVisible());
        this.vBox.setManaged(!vBox.isManaged());
    }

    /**
     * Setter
     *
     * @param parentPart 父节点
     */
    public void setParentPart(Part parentPart) {
        this.parentPart = parentPart;
    }

    /**
     * 转化为左布局
     * @return 转化为左布局后的 LeftMapPart
     */
    public LeftMapPart castToLeft() {
        return castLeafToLeft(this);
    }

    /**
     * 遍历树结构并更换节点布局
     * @param part 当前节点
     * @return 转化结果
     */
    protected LeftMapPart castLeafToLeft(MapPart part) {
        LeftMapPart left = new LeftMapPart(this.root);
        ObservableList<Pair<MapLine, Part>> children = part.getLeaves();

        for (Pair<MapLine, Part> child : children) {
            MapPart mpChild = (MapPart) child.getValue();
            left.addLeaf(mpChild.castToLeft());
        }

        return left;
    }

    public RightMapPart castToRight() {
        return (RightMapPart)this;
    }

    /**
     * Getter
     */
    public NumberBinding getLeftIntersectionY() {
        return leftIntersectionY;
    }

    public SimpleListProperty<Pair<MapLine, Part>> leavesProperty() {
        return leaves;
    }

    public Part getParentPart() {
        return parentPart;
    }

    public AbstractMapNode getRoot() {
        return root;
    }

    public ObservableList<Pair<MapLine, Part>> getLeaves() {
        return leaves.get();
    }

    public Pane getLinesBox() {
        return linesBox;
    }

    public NumberBinding leftIntersectionYProperty() {
        return leftIntersectionY;
    }

    public NodeContextMenu getContext() {
        return context;
    }

    public MapTreeItem getTreeItem() {
        return treeItem;
    }

    public VBox getVBox() {
        return vBox;
    }
}
